package com.swak.router;

import java.util.Optional;

import org.springframework.util.Assert;

/**
 * 构建 RouterFunction 的入口
 * 
 * @author lifeng
 */
public abstract class RouterFunctions {

	public static <T extends Route> RouterFunction<T> path(String path, HandlerFunction<T> handlerFunction) {
		Assert.notNull(path, "'path' must not be null");
		Assert.notNull(handlerFunction, "'handlerFunction' must not be null");
		return new DefaultRouterFunction<T>(RequestPredicates.path(path), handlerFunction);
	}

	public static <T extends Route> RouterFunction<T> route(RequestPredicate predicate,
			HandlerFunction<T> handlerFunction) {
		Assert.notNull(predicate, "'predicate' must not be null");
		Assert.notNull(handlerFunction, "'handlerFunction' must not be null");
		return new DefaultRouterFunction<T>(predicate, handlerFunction);
	}

	static abstract class AbstractRouterFunction<T extends Route> implements RouterFunction<T> {
	}

	static final class FilteredRouterFunction<T extends Route> implements RouterFunction<T> {
		private final RouterFunction<T> routerFunction;
		private final HandlerFilterFunction<T> filterFunction;

		public FilteredRouterFunction(RouterFunction<T> routerFunction, HandlerFilterFunction<T> filterFunction) {
			this.routerFunction = routerFunction;
			this.filterFunction = filterFunction;
		}

		@Override
		public HandlerFunction<T> route(T request) {
			return Optional.of(this.routerFunction.route(request)).map(this.filterFunction::apply).get();
		}

		@Override
		public String toString() {
			return this.routerFunction.toString();
		}
	}

	static final class DifferentComposedRouterFunction<T extends Route> extends AbstractRouterFunction<T> {

		private final RouterFunction<T> first;
		private final RouterFunction<T> second;

		DifferentComposedRouterFunction(RouterFunction<T> first, RouterFunction<T> second) {
			this.first = first;
			this.second = second;
		}

		@Override
		public HandlerFunction<T> route(T request) {
			HandlerFunction<T> hf = first.route(request);
			if (hf != null) {
				return hf;
			}
			return second.route(request);
		}
	}

	static final class ComposedRouterFunction<T extends Route> extends AbstractRouterFunction<T> {
		private final RouterFunction<T> first;
		private final RouterFunction<T> second;

		ComposedRouterFunction(RouterFunction<T> first, RouterFunction<T> second) {
			this.first = first;
			this.second = second;
		}

		@Override
		public HandlerFunction<T> route(T request) {
			HandlerFunction<T> hf = first.route(request);
			if (hf != null) {
				return hf;
			}
			return second.route(request);
		}

		@Override
		public String toString() {
			return String.format("(left %s or right %s)", first, second);
		}
	}

	static final class DefaultRouterFunction<T extends Route> extends AbstractRouterFunction<T> {

		private final RequestPredicate predicate;
		private final HandlerFunction<T> handlerFunction;

		DefaultRouterFunction(RequestPredicate predicate, HandlerFunction<T> handlerFunction) {
			this.predicate = predicate;
			this.handlerFunction = handlerFunction;
		}

		@Override
		public HandlerFunction<T> route(Route request) {
			if (this.predicate.test(request)) {
				return this.handlerFunction;
			}
			return null;
		}

		@Override
		public String toString() {
			return String.format("(predicate %s -> function %s)", predicate.toString(), handlerFunction.description());
		}
	}
}
